﻿using Linq2Rest.Provider;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;
using System.Text;
using System.Threading.Tasks;
using Xinchen.Utils;

namespace Xinchen.SOA.GateWay
{
    public class ServiceCallProxy<T> : RealProxy
    {
        string _serverUrl;
        string _controllerName;
        const string _urlFormat = "http://{0}/{1}/{2}";
        static JsonNetSerializerFactory _factory = new JsonNetSerializerFactory();
        internal ServiceCallProxy(string serverUrl, string controllerName)
            : base(typeof(T))
        {
            _serverUrl = serverUrl;
            _controllerName = controllerName;
        }
        public override System.Runtime.Remoting.Messaging.IMessage Invoke(System.Runtime.Remoting.Messaging.IMessage msg)
        {
            IMethodCallMessage methodCallMessage = (IMethodCallMessage)msg;
            var method = (MethodInfo)methodCallMessage.MethodBase;
            var address = string.Format(_urlFormat, _serverUrl, _controllerName.Split('.').Last(), method.Name);
            var serviceType = method.DeclaringType;
            var parameters = method.GetParameters();
            HttpContent httpContent = null;
            var returnType = method.ReturnType;
            if (returnType.IsGenericType && typeof(IQueryable<>) == returnType.GetGenericTypeDefinition())
            {
                var uri = new Uri(address);
                var jsonRestClient = new JsonNetRestClient(uri);
                var type = typeof(RestContext<>).MakeGenericType(returnType.GetGenericArguments()[0]);
                var client = Activator.CreateInstance(type, jsonRestClient, _factory);
                var query = type.GetProperty("Query").GetValue(client);
                return new ReturnMessage(query, new object[0], 0, methodCallMessage.LogicalCallContext, methodCallMessage);
            }
            else
            {
                if (TypeHelper.IsValueType(parameters[0].ParameterType))
                {
                    //多参数传递，每一个参数都是一个基类型
                    var formParameters = new List<KeyValuePair<string, string>>();
                    var parameterIndex = 0;
                    foreach (var parameter in parameters)
                    {
                        formParameters.Add(new KeyValuePair<string, string>(parameter.Name, Convert.ToString(methodCallMessage.InArgs[parameterIndex++])));
                    }
                    httpContent = new FormUrlEncodedContent(formParameters);
                }
                else
                {
                    //只有一个参数
                    httpContent = new ObjectContent(parameters[0].ParameterType, methodCallMessage.InArgs[0], new JsonMediaTypeFormatter());
                }

                IMethodReturnMessage returnMessage = null;
                var httpClient = new HttpClient();
                httpClient.DefaultRequestHeaders.Add("FromGateway", "1");
                httpClient.DefaultRequestHeaders.Add("ApplicationId", ApplicationContext.Current.ApplicationId);
                HttpResponseMessage result = null;
                using (httpClient)
                {
                    result = httpClient.PostAsync(address, httpContent).Result;
                    if (!result.IsSuccessStatusCode)
                    {
                        throw new HttpRequestException(string.Format("请求地址为{0}的服务时发生错误:{1}", address, result.Content.ReadAsStringAsync().Result));
                    }
                }
                if (TypeHelper.IsValueType(method.ReturnType))
                {
                    var returnString = result.Content.ReadAsStringAsync().Result.TrimStart('"').TrimEnd('"');
                    var objValue = Convert.ChangeType(returnString, method.ReturnType);
                    returnMessage = new ReturnMessage(objValue, new object[0], 0, methodCallMessage.LogicalCallContext, methodCallMessage);
                }
                else
                {
                    var formatter = new JsonMediaTypeFormatter();
                    var obj = formatter.ReadFromStream(method.ReturnType, result.Content.ReadAsStreamAsync().Result, Encoding.UTF8, null);
                    returnMessage = new ReturnMessage(obj, new object[0], 0, methodCallMessage.LogicalCallContext, methodCallMessage);

                }
                return returnMessage;
            }
        }
    }
}
